/*
 *  Cero Project - Copyright   2006 The Cero Developement Team 
 *  (Michael Laguerre, Camille Roux, Matthieu Segret, Mathieu Sivade) 
 * 
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the Free 
 *  Software Foundation; either version 2 of the License, or (at your option) 
 *  any later version.
 * 
 *  This program is distributed in the hope that it will be useful, but 
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 */

package org.ceroproject.games.base;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.ceroproject.SortableList;
import org.ceroproject.Sorter;
import org.ceroproject.games.Card;
import org.ceroproject.games.Zone;
import org.ceroproject.games.ZoneListener;


/**
 * A zone contains cards and is identified by a name.
 * 
 * @author Telem
 */
public abstract class AbstractZone implements Zone {
	/** The name of the zone */
	private String name;
	
	/** The cards' container */
	protected SortableList<Card> cards = new SortableList<Card>();
	
	protected ArrayList<ZoneListener> listeners = new ArrayList<ZoneListener>();

	/**
	 * Creates a new Zone with a given name
	 * 
	 * @param name
	 *            the name of the zone
	 */
	public AbstractZone(String name) {
		super();
		this.name = name;
	}

	/**
	 * Creates a new Zone with a given name and cards to begin with
	 * 
	 * @param name
	 *            the name of the zone
	 * @param cards
	 *            the cards initially contained in the zone
	 */
	public AbstractZone(String name, Collection<Card> cards) {
		super();
		this.name = name;
		for (Card c : cards) {
			this.cards.add(c);
			feedCardIn(null, c);
		}
		this.cards.sort();
	}

	/**
	 * Returns the name of the zone
	 * 
	 * @return the name of the zone
	 */
	public String toString() {
		return name;
	}

	/**
	 * Adds a card to the zone. In most cases, it is better to use the
	 * moveCard function, however.
	 * 
	 * @param card
	 *            the card to add to the zone
	 */
	public void addCard(Card card) {
		addCard(null, card);
	}
	
	/**
	 * Adds a card to the zone at the adequate place, sorts the zone and notifies its listeners
	 * @param from The zone the card came from 
	 * @param card The card added to this zone
	 */
	protected abstract void addCard(Zone from, Card card);

	/**
	 * Removes a card from the zone. In most cases, it is better to use the
	 * moveCard function, however.
	 * 
	 * @param card
	 *            the card to remove from the zone
	 */
	public boolean deleteCard(Card card) {
		return deleteCard(null, card);
	}
	
	protected boolean deleteCard(Zone to, Card card) {
		boolean result = cards.remove(card);
		cards.resort();
		feedCardOut(to, card);
		return result;
	}

	/**
	 * Moves a card from the current zone to another
	 * 
	 * @param card
	 *            The card to move
	 * @param to
	 *            The destination zone
	 */
	public void moveCard(Card card, Zone to) {
		if (deleteCard(to, card)) {
			to.addCard(card);
		}
		else
			throw new RuntimeException("The card " + card
					+ " doesn't belong to the zone " + this);
	}

	/**
	 * Moves all this zone's cards to the zone 'to'
	 * 
	 * @param to
	 *            The zone to move the cards to
	 */
	public void moveCards(Zone to) {
		Collection<Card> allcards = new ArrayList<Card>(this.cards);
		cards.clear();
		//i doubt this will be useful, but you never know
		cards.resort();
		for (Card c : allcards) {
			feedCardOut(this, c);
			to.addCard(c);
		}
	}

	/**
	 * Moves all this zone's cards to the zone 'to'
	 * 
	 * @param cards
	 *            the cards to move from this zone to another
	 * @param to
	 *            The zone to move the cards to
	 */
	public void moveCards(Collection<Card> cards, Zone to) {
		Collection<Card> cardstomove = new ArrayList<Card>(cards);
		this.cards.removeAll(cardstomove);
		this.cards.resort();
		for (Card c : cardstomove){
			feedCardOut(this, c);
			to.addCard(c);
		}
	}

	/**
	 * Returns the name of the zone
	 * 
	 * @return the name of the zone
	 */
	public String getName() {
		return name;
	}
	
	public int getCardCount() {
		return cards.size();
	}

	/**
	 * Add a zone listener
	 * 
	 * @param zoneListener listener to add
	 */
	public void addZoneListener(ZoneListener zoneListener) {
		listeners.add(zoneListener);
	}

	public void removeZoneListener(ZoneListener zoneListener) {
		listeners.remove(zoneListener);
	}

	protected void feedCardIn(Zone from, Card card) {
		for (ZoneListener l : listeners)
			l.cardIn(from, this, card);
	}

	protected void feedCardOut(Zone to, Card card) {
		for (ZoneListener l : listeners)
			l.cardOut(this, to, card);
	}

	public Sorter<Card> getSorter() {
		return cards.getSorter();
	}

	public void setSorter(Sorter<Card> sorter) {
		cards.setSorter(sorter);
	}

	public void sort() {
		cards.sort();
	}

	public void sort(Sorter<Card> sorter) {
		cards.sort(sorter);
	}

	public List<Card> getSortedList() {
		return cards.getSortedList();
	}

	public Iterator<Card> iterator() {
		return cards.getSortedList().iterator();
	}

}
